package com.register.client.domian;


import cn.hutool.json.JSONUtil;
import com.register.client.common.ServiceInstanceOperation;
import com.register.client.start.HttpSender;
import com.register.client.start.RegisterClient;
import com.register.client.utils.SpringContextUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 缓存服务注册表
 * 职责:
 * 1.定时的后台后台线程拉取service的注册表
 * 2.本地存储注册表
 */
public class ClientCachedServiceRegistry {

    public static final Logger log = LoggerFactory.getLogger(ClientCachedServiceRegistry.class);

    //30秒拉取一次注册表
    public static final Long SERVICE_REGISTRY_FETCH_INTERVAL = 10 * 1000L;

    //注册表
    public static Map<String, Map<String, ClientServiceInstance>> registry = new HashMap<>();

    //后台线程
    private Daemon daemon;

    //实例信息

    private RegisterRequest registerRequest = SpringContextUtils.getBean(RegisterRequest.class);;;

    //客户端信息
    private RegisterClient registerClient = SpringContextUtils.getBean(RegisterClient.class);;

    private FetchDeltaRegistryWorker fetchDeltaRegistryWorker;
    //网络组件
    private HttpSender httpSender = SpringContextUtils.getBean(HttpSender.class);


    public static Map<String, Map<String, ClientServiceInstance>> getRegistry() {

        synchronized (registry) {
            return registry;
        }
    }

    public static void setRegistry(Map<String, Map<String, ClientServiceInstance>> registry) {
        ClientCachedServiceRegistry.registry = registry;
    }

    public ClientCachedServiceRegistry(
            RegisterClient registerClient
    ) {
        //1.初始化Daremon
        this.daemon = new Daemon();
        this.registerClient = registerClient;
        this.fetchDeltaRegistryWorker = new FetchDeltaRegistryWorker();

    }

    //初始化启动后台线程
    public void initialize() {
        this.daemon.start();
        this.fetchDeltaRegistryWorker.start();
    }


    //后台注册第一次全量拉取注册表
    public class Daemon extends Thread {

        @Override
        public void run() {
                Map<String, Map<String, ClientServiceInstance>> registry = httpSender.fetchServiceRegistry();
                if (registry != null) {
                    ClientCachedServiceRegistry.registry = registry;
                    log.info("更新注册表[{}]",registry);
                    try {
                        Thread.sleep(SERVICE_REGISTRY_FETCH_INTERVAL);
                    } catch (InterruptedException e) {
                        log.error("等待拉取注册表发生异常[{}]",e);
                    }
                }
                //如果service存在问题,那么重试机制,后面优化
        }
    }

    /**
     * 每隔30秒增量拉取注册表
     */
    public class FetchDeltaRegistryWorker extends Thread {

        @Override
        public void run() {
            for (;;) {
                try {
                    sleep(SERVICE_REGISTRY_FETCH_INTERVAL);
                } catch (InterruptedException e) {
                    log.error("等待拉取增量注册表发生异常[{}]",e);
                }
                Map<String, Map<String, ClientServiceInstance>> fetchDeltaServiceRegistry = httpSender.fetchDeltaServiceRegistry();
                log.info("[{}]拉取增量注册表[{}]",registerRequest.getServiceName(),fetchDeltaServiceRegistry);
                //读写本地注册表,可能会存在并发问题,需要对注册表进行加锁
                synchronized (registry) {
                    mergeDeltaRegistryToRegistry(fetchDeltaServiceRegistry);
                }
            }
        }



        private void mergeDeltaRegistryToRegistry(Map<String, Map<String, ClientServiceInstance>> fetchDeltaServiceRegistry) {
            /**
             *  1.拉取的是最近三分钟有变化的实例,一是注册的,新增的
             *  2.删除的,和本地的注册表进行对比并更新即可
             */
            Set<String> keys = fetchDeltaServiceRegistry.keySet();
            for (String serviceInstanceName : keys) {
                Map<String, ClientServiceInstance> clientServiceInstanceMap = fetchDeltaServiceRegistry.get(serviceInstanceName);
                Set<String> instanceIds = clientServiceInstanceMap.keySet();
                for (String instanceId : instanceIds) {
                    ClientServiceInstance clientServiceInstance = clientServiceInstanceMap.get(instanceId);
                    //注册
                    if (clientServiceInstance.getServiceInstanceOperation().equals(ServiceInstanceOperation.REGISTER.getCode())) {
                        if (registry.get(clientServiceInstance.getServiceName()) == null) {
                            Map<String,ClientServiceInstance> serviceInstanceMap = new HashMap<>();
                            serviceInstanceMap.put(clientServiceInstance.getServiceInstanceId(),clientServiceInstance);
                            registry.put(clientServiceInstance.getServiceName(),serviceInstanceMap);
                            continue;
                        }
                        if (!registry.get(clientServiceInstance.getServiceName()).containsKey(instanceId)) {
                            registry.get(clientServiceInstance.getServiceName()).put(instanceId,clientServiceInstance);
                        }
                    }else {
                        if (registry.get(clientServiceInstance.getServiceName()) != null) {
                            registry.get(clientServiceInstance.getServiceName()).remove(instanceId);
                        }
                    }
                }
            }
        }
    }
}
